// Debug mode on / off
#define DEBUG 0

/************************
 *  Includes
 *************************/

// generic libraries
// LCD library
#include <LiquidCrystal.h>
// builded-in eeprom library
#include <EEPROM.h>
// SPI library for SD and Ethernet
#include <SPI.h>
// Wiznet 500 library
#include <Ethernet.h>

// foreign libraries
// time library
#include <Time.h>
// humidity and temperature sensor
#include <DHT.h>
// SD with FAT16 library
#include <Fat16.h>

// constants and useful variables
#include "data.h"
// useful low-mem functions
#include "fun.h"

/************************
 *     Libraries
 *************************/

// initialize LCD library with pin numbers
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);

// init SD storage 
SdCard card;

// alloc files memory
Fat16 fset; // settings file
Fat16 fcnf; // config file
Fat16 frec; // records file    

// initialize DHT sensor library
DHT dht;

// Ethernet client - connection to web-server
EthernetClient conn;

/************************
 *     Variables
 *************************/
//// SETTINGS
// control delay [sec]
uint16_t ctrl_delay = 10;
// net refresh delay [sec]
uint16_t net_delay = 10;
// net configuration mode
uint8_t net_mode;
// MAC address
byte mac[6];
// IP address
byte ip[4];
// DNS 
byte ns[4];
// Gateway
byte gw[4];
// Subnet mask
byte mask[4];
// Host server IP address
byte host_ip[4];
//user name
char user_name[65];
// access key
char key[33];
// Host server name
char host[65];

//// CONFIG
// recording delay [seconds]
uint16_t rec_delay = 60;
// manual control delay [seconds]
uint16_t man_delay = 60;

//// RECORDS
// records buffer
char env_rec[24];
char act_rec[19];

// control unit data
uint8_t vent_times[32][2]; 
uint8_t wat_times[32][2];
uint8_t vcnt; // ventilation count
uint8_t wcnt; // watering count

boolean auto_vent;
boolean auto_wat;
uint8_t min_hum;
uint8_t max_hum;
uint8_t min_temp;
uint8_t max_temp;
uint8_t min_sm;
uint8_t max_sm;

// DEVICE STATE
// device relais state
boolean watering = false;
boolean ventilation = false;
boolean conn_ok = false;
boolean sendcnf = false;
boolean receivedcnf = false;

// TIMING
// lcd show timer
uint32_t lcd_timer;
// recording timer
uint32_t rec_timer;
// control timer
uint32_t ctrl_timer;
// web check timer
uint32_t net_timer;

// STRING
// String buffer
char buffer[33];


// LCD PAGES
// page number
uint8_t page_nr;




/***********************************************************************************
 ************************************************************************************
 ************************************************************************************
 *
 *          SETUP
 *           init
 *
 ************************************************************************************
 ************************************************************************************
 ************************************************************************************/
void setup() {
#if DEBUG
  Serial.begin(9600);  
#endif

  // init output pins
  pinMode(VENT_PIN, OUTPUT);
  pinMode(WAT_PIN, OUTPUT);
  digitalWrite(VENT_PIN, HIGH);
  digitalWrite(WAT_PIN, HIGH);

  // set pull-up on soil moisture pin
  digitalWrite(SM_PIN, HIGH);

  // init LCD
  lcd.createChar(0, grad);
  lcd.begin(COLS, ROWS);

  // init text
  buffer[0] = '\0';

  lcd.setCursor(0, 0);
  lcd.print(buffer);
  delay(2000);
  lcd.clear();

#if DEBUG
  Serial.println("<SD INIT>");  
#endif

  delay(1000);
  // init SD
  if(!card.init()) {
    error(TXT_SDERR);
  }
#if DEBUG
  Serial.println("<FAT INIT>");  
#endif
  delay(1000);
  if(!Fat16::init(card)) {
    error(TXT_SDERR);
  }

#if DEBUG
  Serial.println("<SD OK>");  
#endif

  // open settings file
  read_text(TXT_SETFILE, buffer); 
  if(!fset.open(buffer, O_READ)) {
    lcd_show(TXT_SETFILE, false);
    error(TXT_FILEERR);
  }

  // parse settings from SETTINGS.TXT
  read_line(&fset, buffer);
  net_mode = read_int(&fset);
  lcd.setCursor(0, 0);
  lcd.print(buffer);
  delay(2000);
  lcd.clear();
#if DEBUG 
  Serial.print("unit name: ");  
  Serial.println(buffer);    

  Serial.print("net mode: ");  
  Serial.println(net_mode);    
#endif

  // try connection if net is set on
  if(net_mode != NET_OFF) 
    connect();
  // close settings file
  fset.close();
  // Get time from user, if isn't connected
  if(!conn_ok) {
    setTime(hour(), minute(), second(), day(), month(), 2014);
    get_date();
    get_time();
  }

  // load config from file
  if(receivedcnf) {
    http_request(HTTP_RECV, NULL);
    receivedcnf = false;
  }
  else {
    load_cnf();
  }


  // Prepare file RECORDS.TXT
  read_text(TXT_RECFILE, buffer); 
  if(!frec.open(buffer, O_CREAT | O_APPEND | O_WRITE) && !frec.close()) {
    lcd_show(TXT_RECFILE, false);
    error(TXT_FILEERR);
  }

  // reset timers
  lcd_timer = rec_timer = ctrl_timer = millis();

  if(conn_ok)
    net_timer = millis();

  // clear display
  lcd.clear();
}


/*****************************************************************************************
 ******************************************************************************************
 ******************************************************************************************
 *
 *              LOOP
 *           run program
 *
 ******************************************************************************************
 ******************************************************************************************
 ******************************************************************************************/
void loop() {
  // check keypad and update display
  if(check_keypad()) {
    show_page();
  }


  // check lcd timer
  if(millis() > lcd_timer + 1000l) {
    show_page(); 
    lcd_timer = millis();
  }

  // check record timer
  if(millis() > rec_timer + (rec_delay * 1000l)) {
    trace_env();
    rec_timer = millis();
  }

  // check keypad and update display
  if(check_keypad()) {
    show_page();
  }

  // check automated control state and control timer
  if(millis() > ctrl_timer + (ctrl_delay * 1000l)) {
    update_control();
    ctrl_timer = millis();
  }

  // check keypad and update display
  if(check_keypad()) {
    show_page();
  }

  // check net refresh timer 
  if(conn_ok && (millis() > net_timer + (net_delay * 1000l))) {
    if(!http_request(HTTP_CHECK, NULL)) {
      conn_ok = false;
    }
    else {
      if(sendcnf) {
        send_cnf();
      }
      if(receivedcnf) {
        http_request(HTTP_RECV, NULL);
        receivedcnf = false;
      }
    }
    net_timer = millis();
  }
}

/***********************************************************************************************************************
 ************************************************************************************************************************
 *
 *                 PROGRAM FUNCTIONS
 *
 ************************************************************************************************************************
 ************************************************************************************************************************/

/********************************************************************************************
 *********************************************************************************************
 *
 *             CONTROL Section
 *
 *********************************************************************************************
 *********************************************************************************************/
void update_control() {
  boolean v_state = false;
  boolean w_state = false;

  // check time control
  ctrl_times(&v_state, &w_state);
  // check auto control
  ctrl_auto(&v_state, &w_state);

  // set outputs
  set_ventilation(v_state);
  set_watering(w_state);
}

void ctrl_times(boolean *vent, boolean *wat) {
  // get current time
  uint16_t curr = (hour() * 60) + minute();
  uint16_t on;
  uint16_t off;

  // check ventilation
  for(byte i = 0; i < vcnt; i++) {
    on = vent_times[i][0] * 10;
    off = on + (vent_times[i][1] * 5);
    if(curr >= on && curr < off) {
      *vent = true;
      break;
    }
  }
  // check watering
  for(byte i = 0; i < wcnt; i++) {
    on = wat_times[i][0] * 10;
    off = on + (wat_times[i][1]);
    if(curr >= on && curr < off) {
      *wat = true;
      break;
    }
  }
}

void ctrl_auto(boolean *vent, boolean *wat) {
  if(auto_vent) {
    dht.read11(DHT_PIN);
    if(dht.humidity <= min_hum) 
      *vent = false;
    if(dht.humidity >= max_hum) 
      *vent = true;
    if(dht.temperature >= max_temp) 
      *vent = true;
    if(dht.temperature <= min_temp) 
      *vent = false;
  }

  if(auto_wat) {
    if(soil_moisture() >= max_sm) 
      *wat = false;
    if(soil_moisture() <= min_sm) 
      *wat = true;
  }
}

void reset_cnf() {
  vcnt = 0;
  wcnt = 0;
  auto_vent = false;
  auto_wat = false;
}

void parse_cnf(char *line) {
#if DEBUG
  Serial.println(line);
#endif
  byte num = 255;
  char *c = line;

  switch(*c) {
  case 'V':
    c++; // get on first space
    num = parse_int(c); // read cnf number
    do { 
      c++; 
    }
    while(*c != ' '); // seek next space
    // check auto data
    if(num < 100) {
      vent_times[vcnt][0] = parse_int(c); // read first value
      do { 
        c++; 
      }
      while(*c != ' '); // seek next space
      vent_times[vcnt][1] = parse_int(c); // read second value
      vcnt++; // increment ventilation times count
    }
    else {
      auto_vent = true;
      switch(num) {
      case 100 :
        min_temp = parse_int(c); // read first value
        do { 
          c++; 
        }
        while(*c != ' '); // seek next space
        max_temp = parse_int(c); // read second value

        break;
      case 101 :
        min_hum = parse_int(c); // read first value
        do { 
          c++; 
        }
        while(*c != ' '); // seek next space
        max_hum = parse_int(c); // read second value
        break;
      }
    }
    break;

  case 'W' :
    c++; // get on first space
    num = parse_int(c); // read cnf number
    do { 
      c++; 
    }
    while(*c != ' '); // seek next space

    // check auto watering
    if(num < 100) {
      wat_times[wcnt][0] = parse_int(c); // read first value
      do { 
        c++; 
      }
      while(*c != ' '); // seek next space
      wat_times[wcnt][1] = parse_int(c); // read second value
      wcnt++; // increment watering times count
    }
    else {
      auto_wat = true;
      switch(num) {
      case 100 :
        min_sm = parse_int(c); // read first value
        do { 
          c++; 
        } 
        while(*c != ' '); // seek next space
        max_sm = parse_int(c); // read second value
        break;
      }
    }
    break;

  case 'D' :
    c++; // get on first space
    num = parse_int(c); // read cnf number
    do { 
      c++; 
    } 
    while(*c != ' '); // seek next space
    switch(num) {
    case 1 :
      rec_delay = parse_int(c);
      break;
    case 2 :
      man_delay = parse_int(c);
      break;
    }
    break;
  }
}

/********************************************************************************************
 *********************************************************************************************
 *
 *             OUTPUT Section
 *
 *********************************************************************************************
 *********************************************************************************************/
void set_ventilation(boolean state) {
  // if state changed
  if(ventilation != state) {
    // set ventilation flag
    ventilation = state;
    // trace auto action
    trace_action(TRIGG_AUTO, ACT_VENT, ventilation ? STATE_ON : STATE_OFF);
    // set output state
    digitalWrite(VENT_PIN, ventilation ? LOW : HIGH);   
  }
}

void set_watering(boolean state) {
  // if state changed
  if(watering != state) {
    // set ventilation flag
    watering = state;
    // trace auto action
    trace_action(TRIGG_AUTO, ACT_WAT, watering ? STATE_ON : STATE_OFF);
    // set output state
    digitalWrite(WAT_PIN, watering ? LOW : HIGH);   
  }
}

/********************************************************************************************
 *********************************************************************************************
 *   
 *             LCD Section
 *
 *********************************************************************************************
 *********************************************************************************************/

/******************************
 *   Show current page on LCD
 *******************************/
void show_page() {
  switch(page_nr) {
  case PAGE_DT :
    two_dig(hour(), buffer);
    buffer[2] = ':';
    two_dig(minute(), buffer + 3);
    buffer[5] = ':';
    two_dig(second(), buffer + 6);
    buffer[8] = '\0';
    lcd.setCursor(4, 0);
    lcd.print(buffer);
    two_dig(day(), buffer);
    buffer[2] = '.';
    two_dig(month(), buffer + 3);
    buffer[5] = '.';
    itoa(year(), buffer + 6, 10);
    buffer[10] = '\0';
    lcd.setCursor(3, 1);
    lcd.print(buffer);
    break;
  case PAGE_TH :
    dht.read11(DHT_PIN);
    read_text(TXT_TEMP, buffer);
    lcd.setCursor(0, 0);
    lcd.print(buffer);
    lcd.setCursor(9, 0);
    lcd.print((int)dht.temperature);
    lcd.write(byte(0));
    lcd.print("C");
    read_text(TXT_HUM, buffer);
    lcd.setCursor(0, 1);
    lcd.print(buffer);
    lcd.setCursor(9, 1);
    lcd.print((int)dht.humidity);
    lcd.print("%");
    break;
  case PAGE_SM :
    read_text(TXT_SOIL, buffer);
    lcd.setCursor(0, 0);
    lcd.print(buffer);
    lcd.setCursor(0, 1);
    lcd.print(soil_moisture());
    lcd.print("%");
    break;
  case PAGE_VW :
    read_text(TXT_WAT, buffer);
    lcd.setCursor(0, 0);
    lcd.print(buffer);
    lcd.setCursor(13, 0);
    watering ? read_text(TXT_ON, buffer) : read_text(TXT_OFF, buffer);
    lcd.print(buffer);
    read_text(TXT_VENT, buffer);
    lcd.setCursor(0, 1);
    lcd.print(buffer);
    lcd.setCursor(13, 1);
    ventilation ? read_text(TXT_ON, buffer) : read_text(TXT_OFF, buffer);
    lcd.print(buffer);
    break;
  case PAGE_NS :
    read_text(TXT_CONN, buffer);
    lcd.setCursor(0, 0);
    lcd.print(buffer);
    if(net_mode == 0) {
      read_text(TXT_OFF, buffer);
    }
    else {
      if(conn_ok) {
        read_text(TXT_CONNOK, buffer);
      }
      else {
        read_text(TXT_CONNERR, buffer);
      }
    }
    lcd.setCursor(0, 1);
    lcd.print(buffer);
    break;
  }
}

/************************
 *  Write on LCD
 *************************/
void lcd_show(byte code, boolean upper) {
  read_text(code, buffer);
  if(upper)
    lcd.setCursor(0,0);
  else
    lcd.setCursor(0,1);
  lcd.print(buffer);
  for(byte i = 0; i < 16; i++)
    lcd.print(' ');
}

/************************
 *  Write error on LCD
 * and stop program
 *************************/
void error(byte code) {
  lcd_show(code, true);
  while(1);
}

/************************
 *  Write info on LCD
 *************************/
void info(byte code, boolean upper) {
  lcd_show(code, upper);
  delay(2000);
}

/*********************************************************************************************
 **********************************************************************************************
 *    
 *      KEYPAD Section
 *
 **********************************************************************************************
 *********************************************************************************************/

/******************
 * Read keypad pin
 *******************/
byte get_key() {
  uint16_t key = analogRead(KEYPAD_PIN);
  key = key >> 6;
  return key;
}

/******************
 * Check user input
 *******************/
boolean check_keypad() {
  switch(get_key()) {
  case KEY_LEFT :
    page_nr = page_nr > 0 ? page_nr - 1 : PAGES - 1;
    lcd.clear();
    while(get_key() != KEY_NONE);
    return true;
  case KEY_RIGHT :
    page_nr++;
    page_nr %= PAGES;
    lcd.clear();
    while(get_key() != KEY_NONE);
    return true;
  case KEY_UP :
    switch(page_nr) {
    case PAGE_VW :
      watering = !watering;
      digitalWrite(WAT_PIN, watering ? LOW : HIGH);
      // trace action
      trace_action(TRIGG_MAN, ACT_WAT, watering ? STATE_ON : STATE_OFF);

      // add manual delay to control timer
      ctrl_timer = millis() + (man_delay * 1000l);
      break;
    case PAGE_DT :
      get_time();
      break;          
    }
    while(get_key() != KEY_NONE);      
    return true;
  case KEY_DOWN :
    switch(page_nr) {
    case PAGE_VW :
      ventilation = !ventilation;
      digitalWrite(VENT_PIN, ventilation ? LOW : HIGH);
      // trace action
      trace_action(TRIGG_MAN, ACT_VENT, ventilation ? STATE_ON : STATE_OFF);
      // add manual delay to control timer
      ctrl_timer = millis() + (man_delay * 1000l);
      break;
    case PAGE_DT :
      get_date();
      break;                    
    }
    while(get_key() != KEY_NONE);      
    return true;
  case KEY_SELECT :
    switch(page_nr) {
    case PAGE_NS :
      // if connection error ocureed, try reconnect
      if(net_mode > 0 && !conn_ok) {
        reconnect();
      }
      break;
    }
  }
  return false;
}

/**************************************
 *  get time from user
 ***************************************/
void get_time() {
  byte h = hour();
  byte m = minute();
  byte s = second();

  byte cp[] = {
    5, 8, 11                                        };  // cursor positions
  byte c_p = 0;  // cursor position
  /*
     * set time
   */

  // set blinking cursor
  lcd.cursor();  

  // show settings page
  lcd.clear();
  read_text(TXT_SETTIME, buffer);
  lcd.print(buffer);

  // wait for release all keys
  while(get_key() != KEY_NONE) {
  };

  // prepare text buffer
  for(byte i = 0; i < 4; i++)
    buffer[i] = ' ';

  // set time 
  boolean settime = true;
  while(settime) {  
    // show time
    lcd.setCursor(0,1);

    two_dig(h, buffer + 4);
    buffer[6] = ':';
    two_dig(m, buffer + 7);
    buffer[9] = ':';  
    two_dig(s, buffer + 10);
    buffer[12] = '\0';
    lcd.print(buffer);

    // set cursor position
    lcd.setCursor(cp[c_p], 1);

    // get user input
    while(get_key() == KEY_NONE) {
    };
    switch(get_key()) {
    case KEY_UP : 
      switch(c_p) {
      case 0 :
        h++;
        h %= 24;
        break;

      case 1 :
        m++;
        m %= 60;
        break;

      case 2:
        s++;
        s %= 60;
        break;          
      }
      break;
    case KEY_DOWN :
      switch(c_p) {
      case 0 :
        h = h == 0 ? 23 : h - 1;
        break;

      case 1 :
        m = m == 0 ? 59 : m - 1;
        break;

      case 2:
        s = s == 0 ? 59 : s - 1;
        break;          
      }
      break;
    case KEY_LEFT :
      c_p = c_p == 0 ? 2 : c_p - 1;
      break;
    case KEY_RIGHT :
      c_p++;
      c_p %= 3;
      break;
    case KEY_SELECT :
      settime = false;
      break;
    case KEY_NONE :
      break;
    }
    delay(200);
  }
  // set system time
  setTime(h, m, s, day(), month(), year());
  lcd.noCursor();
  lcd.clear();
}

/**************************************
 *  get date from user
 ***************************************/
void get_date() {
  byte cp[] = {
    4, 7, 12                                        };  // cursor positions
  byte c_p = 0;  // cursor position
  byte d = day();     // day
  byte m = month();    // month
  uint16_t y = year();  // year

  /*
     * set date
   */

  // set blinking cursor
  lcd.cursor();  

  // show settings page
  lcd.clear();
  read_text(TXT_SETDATE, buffer);
  lcd.print(buffer);

  // wait for release all keys
  while(get_key() != KEY_NONE) {
  };

  // prepare text buffer
  for(byte i = 0; i < 3; i++)
    buffer[i] = ' ';

  // set date 
  boolean setdate = true;
  while(setdate) {
    // show date
    lcd.setCursor(0,1);
    two_dig(d, buffer + 3);
    buffer[5] = '.';
    two_dig(m, buffer + 6);
    buffer[8] = '.';  
    itoa(y, buffer + 9, 10);
    lcd.print(buffer);

    // set cursor position
    lcd.setCursor(cp[c_p], 1);

    // get user input
    while(get_key() == KEY_NONE) {
    };
    switch(get_key()) {
    case KEY_UP : 
      switch(c_p) {
      case 0 :
        d++;
        if(m == 2)
          d = d > 28 ? 1 : d;
        else if(m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12)
          d = d > 31 ? 1 : d;
        else
          d = d > 30 ? 1 : d;
        break;

      case 1 :
        m++;
        m = m > 12 ? 1 : m;
        break;

      case 2:
        y++;
        break;          
      }
      break;

    case KEY_DOWN :
      switch(c_p) {
      case 0 :
        d--;
        if(m == 2)
          d = d < 1 ? 28 : d;
        else if(m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12)
          d = d < 1  ? 31 : d;
        else
          d = d < 1 ? 30 : d;
        break;

      case 1 :
        m--;
        m = m < 1 ? 12 : m;
        break;

      case 2:
        y--;
        y = y < 2014 ? 2014 : y;
        break;          
      }
      break;

    case KEY_LEFT :
      c_p = c_p == 0 ? 2 : c_p - 1;
      break;

    case KEY_RIGHT :
      c_p++;
      c_p %= 3;
      break;

    case KEY_SELECT :
      setdate = false;
      break;
    case KEY_NONE :
      break;
    }
    delay(200);
  }
  // check correct date
  if(m == 2)
    d = d > 28 ? 28 : d;
  else if(m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12)
    d = d > 31 ? 31 : d;
  else
    d = d > 30 ? 30 : d;

  // set system date
  setTime(hour(), minute(), second(), d, m, y);
  lcd.noCursor();
  lcd.clear();
}


/******************************************************************************************************
 *******************************************************************************************************
 *
 *           FILES Section
 *
 *******************************************************************************************************
 *******************************************************************************************************/

/*************************************
 * load config data from file
 **************************************/
void load_cnf() {
  read_text(TXT_CNFFILE, buffer);
  if(!fcnf.open(buffer, O_READ)) {
    lcd_show(TXT_CNFFILE, false);
    error(TXT_FILEERR);
  }

  reset_cnf();
  while(read_line(&fcnf, buffer)) {
    parse_cnf(buffer);
  }
  fcnf.close();
}

/*************************************
 * save config data to file
 **************************************/
void save_cnf() {
  read_text(TXT_CNFFILE, buffer);
  fcnf.open(buffer, O_CREAT | O_TRUNC | O_WRITE);
  char *p;
  // write ventilation times
  for(byte i = 0; i < vcnt; i++) {
    p = buffer;
    *p = 'V';
    p++;
    *p = ' ';
    p++;
    two_dig(i + 1, p);
    p += 2;
    *p = ' ';
    p++;
    three_dig(vent_times[i][0], p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(vent_times[i][1], p);
    p += 3;
    *p = '\0';
    fcnf.println(buffer);
  }

  if(auto_vent) {
    p = buffer;
    *p = 'V';
    p++;
    *p = ' ';
    p++;
    three_dig(100, p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(min_temp, p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(max_temp, p);
    p += 3;
    *p = '\0';
    fcnf.println(buffer);

    p = buffer;
    *p = 'V';
    p++;
    *p = ' ';
    p++;
    three_dig(101, p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(min_hum, p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(max_hum, p);
    p += 3;
    *p = '\0';
    fcnf.println(buffer);
  }

  // watering data
  for(byte j = 0; j < wcnt; j++) {
    p = buffer;
    *p = 'W';
    p++;
    *p = ' ';
    p++;
    two_dig(j + 1, p);
    p += 2;
    *p = ' ';
    p++;
    three_dig(wat_times[j][0], p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(wat_times[j][1], p);
    p += 3;
    *p = '\0';
    fcnf.println(buffer);
  }

  if(auto_wat) {
    p = buffer;
    *p = 'W';
    p++;
    *p = ' ';
    p++;
    three_dig(100, p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(min_sm, p);
    p += 3;
    *p = ' ';
    p++;
    three_dig(max_sm, p);
    p += 3;
    *p = '\0';
    fcnf.println(buffer);
  }

  // delay data
  p = buffer;
  *p = 'D';
  p++;
  *p = ' ';
  p++;
  *p = '1';
  p++;
  *p = ' ';
  p++;
  three_dig(rec_delay, p);
  p += 3;
  *p = '\0';
  fcnf.println(buffer);

  p = buffer;
  *p = 'D';
  p++;
  *p = ' ';
  p++;
  *p = '2';
  p++;
  *p = ' ';
  p++;
  three_dig(man_delay, p);
  p += 3;
  *p = '\0';
  fcnf.println(buffer);

  fcnf.close();
}

/*************************************
 * save traced data to file
 **************************************/
void save_rec(char *rec) {
  read_text(TXT_RECFILE, buffer);
  frec.open(buffer, O_CREAT | O_APPEND | O_WRITE);
  frec.println(rec);
  frec.close();

}


/******************************************************************************************************
 *******************************************************************************************************
 *
 *           TRACE Section
 *
 *******************************************************************************************************
 *******************************************************************************************************/

/*************************************
 * create web-friendly date time format
 **************************************/
void web_dt(char *text) {
  two_dig(hour(), text);
  text += 2;
  two_dig(minute(), text);
  text += 2;
  two_dig(second(), text);
  text += 2;
  itoa(year(), text, 10);
  text += 4;
  two_dig(month(), text);
  text += 2;
  two_dig(day(), text);
}

/***************************************
 * trace environmental conditions to file
 ***************************************/
void trace_env() {
  dht.read11(DHT_PIN);
  char *p = env_rec;
  *p = 'E';
  p++;
  web_dt(p);
  p += 14;
  two_dig((byte)dht.temperature, p);
  p += 2;
  three_dig((byte)dht.humidity, p);
  p += 3;
  three_dig(soil_moisture(), p);
  *(p + 3) = '\0';

#if DEBUG
  Serial.println(env_rec);
#endif

  // save record to file
  save_rec(env_rec);

  // save record to web server, if is available
  if(conn_ok) {
    if(!http_request(HTTP_SEND, env_rec)) {
      conn_ok = false;
    }
  }
}

/**********************************
 *  trace actions to statistics file
 ***********************************/
void trace_action(char trigg, char action, char state) {
  char *p = act_rec;
  *p = 'A';
  p++;
  web_dt(p);
  p += 14;
  *p = trigg;
  p ++;
  *p = action;
  p ++;
  *p = state;
  *(p + 1) = '\0';

#if DEBUG
  Serial.println(act_rec);
#endif

  // save record to file
  save_rec(act_rec);

  // save record to web server, if is available
  if(conn_ok) {
    if(!http_request(HTTP_SEND, act_rec)) {
      conn_ok = false;
    }
  }
}


/******************************************************************************************************
 *******************************************************************************************************
 *
 *             WEB Section
 *
 *******************************************************************************************************
 *******************************************************************************************************/


/***************************
 * Send config data to web db
 ****************************/
void send_cnf() {
  lcd.clear();
  lcd_show(TXT_SENDDATA, true);
  char *p;

  // send clear config command
  p = buffer;
  *p = 'C';
  p++;
  *p = '\0';
  if(conn_ok) {
    if(!http_request(HTTP_SEND, buffer)) {
      conn_ok = false;
      return;
    }
  }


  // send ventilation times
  for(byte i = 0; i < vcnt; i++) {
    p = buffer;
    *p = 'V';
    p++;
    *p = '_';
    p++;
    two_dig(i + 1, p);
    p += 2;
    *p = '_';
    p++;
    three_dig(vent_times[i][0], p);
    p += 3;
    *p = '_';
    p++;
    three_dig(vent_times[i][1], p);
    p += 3;
    *p = '\0';
    if(conn_ok) {
      if(!http_request(HTTP_SEND, buffer)) {
        conn_ok = false;
        return;
      }
    }
  }

  if(auto_vent) {
    p = buffer;
    *p = 'V';
    p++;
    *p = '_';
    p++;
    three_dig(100, p);
    p += 3;
    *p = '_';
    p++;
    three_dig(min_temp, p);
    p += 3;
    *p = '_';
    p++;
    three_dig(max_temp, p);
    p += 3;
    *p = '\0';
    if(conn_ok) {
      if(!http_request(HTTP_SEND, buffer)) {
        conn_ok = false;
        return;
      }
    }

    p = buffer;
    *p = 'V';
    p++;
    *p = '_';
    p++;
    three_dig(101, p);
    p += 3;
    *p = '_';
    p++;
    three_dig(min_hum, p);
    p += 3;
    *p = '_';
    p++;
    three_dig(max_hum, p);
    p += 3;
    *p = '\0';
    if(conn_ok) {
      if(!http_request(HTTP_SEND, buffer)) {
        conn_ok = false;
        return;
      }
    }
  }

  // watering data
  for(byte j = 0; j < wcnt; j++) {
    p = buffer;
    *p = 'W';
    p++;
    *p = '_';
    p++;
    two_dig(j + 1, p);
    p += 2;
    *p = '_';
    p++;
    three_dig(wat_times[j][0], p);
    p += 3;
    *p = '_';
    p++;
    three_dig(wat_times[j][1], p);
    p += 3;
    *p = '\0';
    if(conn_ok) {
      if(!http_request(HTTP_SEND, buffer)) {
        conn_ok = false;
        return;
      }
    }
  }

  if(auto_wat) {
    p = buffer;
    *p = 'W';
    p++;
    *p = '_';
    p++;
    three_dig(100, p);
    p += 3;
    *p = '_';
    p++;
    three_dig(min_sm, p);
    p += 3;
    *p = '_';
    p++;
    three_dig(max_sm, p);
    p += 3;
    *p = '\0';
    if(conn_ok) {
      if(!http_request(HTTP_SEND, buffer)) {
        conn_ok = false;
        return;
      }
    }
  }

  // delay data
  p = buffer;
  *p = 'D';
  p++;
  *p = '_';
  p++;
  *p = '1';
  p++;
  *p = '_';
  p++;
  three_dig(rec_delay, p);
  p += 3;
  *p = '\0';
  if(conn_ok) {
    if(!http_request(HTTP_SEND, buffer)) {
      conn_ok = false;
      return;
    }
  }

  p = buffer;
  *p = 'D';
  p++;
  *p = '_';
  p++;
  *p = '2';
  p++;
  *p = '_';
  p++;
  three_dig(man_delay, p);
  p += 3;
  *p = '\0';
  if(conn_ok) {
    if(!http_request(HTTP_SEND, buffer)) {
      conn_ok = false;
      return;
    }
  }

  sendcnf = false;
  lcd.clear();
}

/**************************
 *   try reconnection
 **************************/
void reconnect() {
  // open settings file
  read_text(TXT_SETFILE, buffer); 
  if(!fset.open(buffer, O_READ)) {
    lcd_show(TXT_SETFILE, false);
    error(TXT_FILEERR);
  }

  // skip other settings from SETTINGS.TXT
  read_line(&fset, buffer);
  net_mode = read_int(&fset);
  connect();
  fset.close();
}


/**************************
 *   connect to web server
 **************************/
void connect() {
  // read net configuration
  switch(net_mode) {
  case NET_MASK : 
    read_addr(&fset, mask, 4);
  case NET_GW :   
    read_addr(&fset, gw, 4);
  case NET_DNS :  
    read_addr(&fset, ns, 4);
  case NET_IP :   
    read_addr(&fset, ip, 4);
  case NET_DHCP : 
    read_addr(&fset, mac, 6);
    break;
  }
  // read key and user name from file
  read_line(&fset, user_name);
  read_line(&fset, key);
  // read host ip
  read_addr(&fset, host_ip, 4);
  // read host name
  read_line(&fset, host);

  lcd.clear();
  info(TXT_INFCONN, true);   // write info on LCD
  delay(3000);  // give Wiznet few seconds to initialize

  // start Ethernet module with selected mode  
  switch(net_mode) {
  case NET_DHCP :
    if(!Ethernet.begin(mac))
      info(TXT_DHCPERR, false); // Show DHCP error on LCD

#if DEBUG
    Serial.println(" DHCP connected ");
#endif

    break; 
  case NET_IP :
    Ethernet.begin(mac, ip);
    break;
  case NET_DNS :
    Ethernet.begin(mac, ip, ns);
    break;
  case NET_GW :
    Ethernet.begin(mac, ip, ns, gw);
    break;
  case NET_MASK :
    Ethernet.begin(mac, ip, ns, gw, mask);
    break; 
  }

  delay(3000);  // give Wiznet500 few seconds to connect

  // try to connect with server
  if(http_request(HTTP_CHECK, NULL)) {
    info(TXT_CONNOK, false);
  }
  else {
    info(TXT_CONNERR, false);
    conn_ok = false;
  }
}


/**********************
 *  Send http request
 ***********************/
boolean http_request(byte action, char *data) {
  // connect to server
#if DEBUG
  Serial.println("connecting...");
#endif
  if(!conn.connected())
    if(!conn.connect(host_ip, 80)) {
#if DEBUG
      Serial.println("connection failed");
#endif
      conn.stop();
      return false;
    }
#if DEBUG
  Serial.println("connected");
#endif

  // set connection flag
  conn_ok = true;

  // **************
  // send a request
  // **************

  eeprom2http(REQ_HEAD1, &conn);
  switch(action) {
  case HTTP_CHECK :
    eeprom2http(REQ_CHECK, &conn);
    break;
  case HTTP_SEND :
    eeprom2http(REQ_SEND, &conn);
    conn.print(data);
#if DEBUG
    Serial.println(data);
#endif

    break;
  case HTTP_RECV :
    eeprom2http(REQ_RECV, &conn);
    break;
  }
  eeprom2http(REQ_KEY, &conn);
  conn.print(key);
#if DEBUG
  Serial.println(key);
#endif
  eeprom2http(REQ_NAME, &conn);
  conn.print(user_name);
#if DEBUG
  Serial.println(user_name);
#endif

  eeprom2http(REQ_HEAD2, &conn);
  conn.println();
  eeprom2http(REQ_HOST, &conn);
  conn.println(host);
  eeprom2http(REQ_CONN1, &conn);
  eeprom2http(REQ_CONN2, &conn);
  conn.println();
  conn.println();
#if DEBUG
  Serial.println("request sent");
#endif

  // ******

  // wait for data availablity
  while(!conn.available())
    if(!conn.connected()) {
      conn.stop();
      conn_ok = false;
      return false;
    }

  // get response
  return http_response(&action);
}


/**********************
 * Parse http response
 ***********************/
boolean http_response(byte *action) {
  if(*action == HTTP_CHECK) {
    while(conn.connected()) {
#if DEBUG
      Serial.println("receiving");
#endif
      switch(get_tag(&conn)) {
      case TAG_TIME :
        setTime(http_int(&conn));
        break;

      case TAG_AUTH :
        if(http_int(&conn) != AUTH_OK) {

          conn.stop();
          return false;
        }
        break;

      case TAG_SEND :
        sendcnf = true;
        break;

      case TAG_RECV :
        lcd.clear();
        lcd_show(TXT_RECVDATA, true);
        reset_cnf();
        http_line(&conn, buffer); // read first line with tag to the end
        while(conn.connected()) {
          http_line(&conn, buffer);
          parse_cnf(buffer);
        }
        conn.stop();
        save_cnf();
        receivedcnf = true;
        lcd.clear();
        return true;
        break;

      case TAG_END :  
        while(conn.available())
          conn.read();
        conn.stop();
        return true;
        break;
      }
    }
    return false;
  }
  else {
    boolean result = get_tag(&conn) == TAG_OK ? true : false;
    while(conn.available())
      conn.read();
    conn.stop();    
    return result;
  }
}











































